
module Controller(
  input  wire        clk,
  input  wire        rst_n,
  input  wire        clken,
  
  input  wire [ 4:0] slot,
  input  wire [ 1:0] stage,

  //CPU Interface
  input  wire        WE_n,
  input  wire        A,
  input  wire [ 7:0] D,

  output reg         am,
  output reg         pm,
  output reg         wf,
  output reg  [ 3:0] ml,
  output reg  [ 6:0] tl,
  output reg  [ 2:0] fb,
  output reg  [ 3:0] ar,
  output reg  [ 3:0] dr,
  output reg  [ 3:0] sl,
  output reg  [ 3:0] rr,
  output reg  [ 2:0] blk,
  output reg  [ 8:0] fnum,
  output reg  [ 3:0] rks,
  
  output reg         key,
  output reg         rhythm
); 

reg [7:0] addr;
reg [7:0] data;
reg       wr;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        addr <= #1 8'b0;
	wr   <= #1 1'b0;
	data <= #1 8'b0;
    end
    else if(clken) begin
        addr <= #1 addr;
	wr   <= #1 wr;
	data <= #1 data;
    end
    else if((!WE_n) && (!A))begin
        addr <= #1 D;
	wr   <= #1 1'b0;
    end
    else if((!WE_n) && A)begin
	wr   <= #1 1'b1;
	data <= #1 D;
    end
    else begin
	wr <= #1 1'b0;
    end
end

reg         regs_wr;
reg  [ 3:0] regs_addr;
reg  [21:0] regs_wdata;
wire [21:0] regs_rdata;
RegisterMemory U_RegisterMemory(
    .clk   ( clk        ) ,
    .rst_n ( rst_n      ) ,
    .addr  ( regs_addr  ) ,
    .wr    ( regs_wr    ) ,
    .idata ( regs_wdata ) ,
    .odata ( regs_rdata )   
);

reg  [35:0] user_voice_wdata;
reg         user_voice_wr;
reg  [ 5:0] user_voice_addr;
reg  [ 5:0] slot_voice_addr;
wire [35:0] user_voice_rdata;
wire [35:0] slot_voice_data;
VoiceMemory U_VoiceMemory(
    .clk    ( clk              ) ,
    .rst_n  ( rst_n            ) ,
    .idata  ( user_voice_wdata ) ,
    .wr     ( user_voice_wr    ) ,
    .rwaddr ( user_voice_addr  ) ,
    .roaddr ( slot_voice_addr  ) ,
    .odata  ( user_voice_rdata ) ,
    .rodata ( slot_voice_data  )    
);

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	regs_addr <= #1 4'b0;
    else if(clken)
	regs_addr <= #1 regs_addr;
    else if(stage == 2'd0)
	regs_addr <= #1 slot[4:1];  
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	regs_wr <= #1 1'b0;
    else if(clken)
	regs_wr <= #1 regs_wr;
    else if(stage == 2'd0)
	regs_wr <= #1 1'b0;
    else if((stage == 2'd2) && wr)
	if((addr >= 8'd16) && (addr <= 8'd56))
	    if((addr[3:0] == slot[4:1]) && (addr[4]|addr[5]))
		 regs_wr <= #1 1'b1;
	    else
		 regs_wr <= #1 1'b0;
    else
         regs_wr <= #1 regs_wr;	    
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	 regs_wdata <= #1 22'b0;
    else if(clken)
	 regs_wdata <= #1 regs_wdata;
    else if((stage == 2'd2) && wr)
	 if((addr>=8'd16) && (addr<=8'd56))
             if(addr[3:0]==slot[4:1])
		case(addr[5:4])
                2'b01  : regs_wdata <= #1 {regs_rdata[21:8], data};
	        2'b10  : regs_wdata <= #1 {regs_rdata[21:14], data[5:0], regs_rdata[7:0]};
	        2'b11  : regs_wdata <= #1 {data, regs_rdata[13:0]};
	        default: regs_wdata <= #1 regs_rdata;	
		endcase	
end

reg extra_mode;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	extra_mode <= #1 1'b0;
    else if(clken)
	extra_mode <= #1 extra_mode;
    else if(wr && (addr==8'd240))
	extra_mode <= #1 (data==8'h80); 
end 

reg[5:0] vindex;
reg[5:0] vindex_hold;
always @(*)begin
    if(stage == 2'd0)
	if(extra_mode==1'b0)
            vindex = {5'b0,slot[0]};
	else if(vindex_hold==6'd37)
	    vindex = 6'd0;
        else	
            vindex = vindex_hold + 1'b1;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	vindex_hold <= #1 6'b0;
    else
        vindex_hold <= #1 vindex;
end

//Voice block
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	user_voice_addr <= #1 6'b0;
    else if(clken)
	user_voice_addr <= #1 user_voice_addr;
    else 
	user_voice_addr <= #1 vindex;
end

wire[7:0] addrtmp = addr - 8'd64;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	user_voice_wr <= #1 1'b0;
    else if(clken)
	user_voice_wr <= #1 user_voice_wr;
    else if(stage == 2'd0)
	user_voice_wr <= #1 1'b0;
    else if((stage==2'd2) && wr)
        if(((extra_mode==1'b0) && (addr<8'd8)) || (extra_mode && (addrtmp[7:3]==vindex[5:1])))	    
	    user_voice_wr <= #1 1'b1;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	user_voice_wdata <= #1 36'b0;
    else if(clken)
	user_voice_wdata <= #1 user_voice_wdata;
    else if((stage==2'd2) && wr)
        if(((extra_mode==1'b0) && (addr<8'd8)) || (extra_mode && (addrtmp[7:3]==vindex[5:1])))	    
            case(addr[2:1])
            2'b00: if(addr[0]==vindex[0]) 
		       user_voice_wdata <= #1 {data,user_voice_rdata[27:0]};
	    2'b01: if((addr[0]==1'b0) && (vindex[0]==1'b0))
		       user_voice_wdata <= #1 {user_voice_rdata[35:28], data, user_voice_rdata[19:0]};
	           else if((addr[0]==1'b1) && (vindex[0]==1'b0))
		       user_voice_wdata <= #1 {user_voice_rdata[35:20], data[3:0], user_voice_rdata[15:0]};
	           else if((addr[0]==1'b1) && (vindex[0]==1'b1))
		       user_voice_wdata <= #1 {user_voice_rdata[35:28], data[7:6], user_voice_rdata[25:20], data[4], user_voice_rdata[18:0]};
            2'b10: if(addr[0]==vindex[0])
		       user_voice_wdata <= #1 {user_voice_rdata[35:16], data,user_voice_rdata[7:0]};
            2'b11: if(addr[0]==vindex[0])
		       user_voice_wdata <= #1 {user_voice_rdata[35:8], data};
	    endcase
end

reg[5:0] slot_tmp;
always @(*)begin
    case(slot[4:1])
    4'h0: slot_tmp = 6'b000000;
    4'h1: slot_tmp = 6'b011000;
    4'h2: slot_tmp = 6'b100000;
    4'h3: slot_tmp = 6'b100101;
    4'h4: slot_tmp = 6'b101000;
    4'h5: slot_tmp = 6'b101011;
    4'h6: slot_tmp = 6'b101101;
    4'h7: slot_tmp = 6'b101111;
    4'h8: slot_tmp = 6'b110000;
    4'h9: slot_tmp = 6'b110010;
    4'hA: slot_tmp = 6'b110011;
    4'hB: slot_tmp = 6'b110100;
    4'hC: slot_tmp = 6'b110101;
    4'hD: slot_tmp = 6'b110110;
    4'hE: slot_tmp = 6'b110111;
    4'hF: slot_tmp = 6'b111000;
    endcase
end

reg[10:0] rflag;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
	slot_voice_addr <= #1 6'b0;
    else if(!clken)
	slot_voice_addr <= #1 slot_voice_addr;
    else if(stage==2'd0)
	if(rflag[5] && (slot<=5'd12))
	    slot_voice_addr <= #1 {1'b0, slot} + 5'd20;
	else
            slot_voice_addr <= #1 {slot_tmp, 1'b0} + slot[0];
end








endmodule


